﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 发送端
{
    static class GeLi_ContronalParaMeter
    {
        public struct ACPara
        {
            public int temp;
            public GeLi_ContronalParaMeter.Mode Mode;
            public GeLi_ContronalParaMeter.Power Power;
            public GeLi_ContronalParaMeter.ShowTemp ShowTemp;
            public GeLi_ContronalParaMeter.Dry Dry;
            public GeLi_ContronalParaMeter.Light Light;
            public GeLi_ContronalParaMeter.Sleep Sleep;
            public GeLi_ContronalParaMeter.UltraStrong UltraStrong;
            public GeLi_ContronalParaMeter.Sweep Sweep;
            public GeLi_ContronalParaMeter.Speed Speed;
            public GeLi_ContronalParaMeter.Health Health;
        }

        /// <summary>
        /// 设定温度超出范围异常
        /// </summary>
        public class TemperatureOutOfRange : ApplicationException
        {
            public TemperatureOutOfRange() : base("温度超出范围，温度值必须在16-30摄氏度之间！") { }
        }

        /// <summary>
        /// 空调模式
        /// 1-3位：模式
        /// </summary>
        public enum Mode : uint
        {
            自动 = 0,
            //图标：循环箭头。代码：000。
            制热 = 1,
            //图标：太阳。代码：001
            除湿 = 2,
            //图标：水滴。代码：010。
            制冷 = 4,
            //图标：雪花。代码：100。
            送风 = 6,
            //图标：风扇。代码：110。
        }

        /// <summary>
        /// 电源
        /// 4位
        /// </summary>
        public enum Power : uint
        {
            开机 = 1,
            关机 = 0
        }
        /// <summary>
        /// 5-6位：风速
        /// </summary>
        public enum Speed : uint
        {
            自动 = 0,
            一级 = 2,
            二级 = 1,
            三级 = 3
        }
        /// <summary>
        /// 扫风
        /// 7、37、41位（加65位）：扫风
        /// 第65位取反
        /// </summary>
        public enum Sweep : uint
        {
            上下 = 6,
            左右 = 5,
            上下左右 = 7,
            无扫风 = 0
        }

        /// <summary>
        /// 睡眠
        /// 8位：睡眠
        /// </summary>
        public enum Sleep : uint
        {
            //睡眠：1
            //不睡眠：0
            睡眠 = 1,
            不睡眠 = 0
        }

        /// <summary>
        /// 将温度转换为二进制表示
        /// </summary>
        public class Temperature
        {
            private static readonly byte[] COLDMODE_PRE = new byte[15] { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7 };
            private static readonly byte[] COLDMODE_SUF = new byte[15] { 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15, 0, 8, };

            private static readonly byte[] HOTMODE_PRE = new byte[15] { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 13, 11, 7, };
            private static readonly byte[] HOTMODE_SUF = new byte[15] { 6, 14, 1, 9, 5, 13, 3, 11, 7, 15, 0, 8, 4, 12, 2, };

            private static readonly byte[] DRYMODE_PRE = new byte[15] { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, };
            private static readonly byte[] DRYMODE_SUF = new byte[15] { 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15, 0, 8, 4, };

            private static readonly byte[] AIRMODE_PRE = new byte[15] { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, };
            private static readonly byte[] AIRMODE_SUF = new byte[15] { 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15, 0, 8, 4, 12, };

            public static uint GetPreCode(Mode m, int temp)
            {
                if (temp >= 16 && temp <= 30)
                {
                    temp -= 16;
                }
                else
                {
                    throw new TemperatureOutOfRange();
                }
                switch (m)
                {
                    case Mode.制冷:
                        return COLDMODE_PRE[temp];
                    case Mode.制热:
                        return HOTMODE_PRE[temp];
                    case Mode.送风:
                        return AIRMODE_PRE[temp];
                    case Mode.除湿:
                        return DRYMODE_PRE[temp];
                    case Mode.自动:
                        return 0;
                    default:
                        return 0;
                }
            }

            public static uint GetSufCode(Mode m, int temp)
            {
                if (temp >= 15 && temp <= 30)
                {
                    temp -= 15;
                }
                else
                {
                    throw new TemperatureOutOfRange();
                }
                switch (m)
                {
                    case Mode.制冷:
                        return COLDMODE_SUF[temp];
                    case Mode.制热:
                        return HOTMODE_SUF[temp];
                    case Mode.送风:
                        return AIRMODE_SUF[temp];
                    case Mode.除湿:
                        return DRYMODE_SUF[temp];
                    case Mode.自动:
                        return 0;
                    default:
                        return 0;
                }
            }
        }

        /// <summary>
        /// 超强模式
        /// </summary>
        public enum UltraStrong : uint
        {
            普通 = 0,
            超强 = 1
        }
        /// <summary>
        /// 灯光
        /// </summary>
        public enum Light : uint
        {
            亮 = 1,
            灭 = 0
        }

        /// <summary>
        /// 健康，换气
        /// </summary>
        public enum Health : uint
        {
            健康 = 2,
            换气 = 1,
            健康换气 = 3,
            普通 = 0
        }
        /// <summary>
        /// 干燥
        /// 制冷模式下-干燥；制热模式下-辅热；
        /// </summary>
        public enum Dry : uint
        {
            干燥 = 1,
            普通 = 0
        }
        /// <summary>
        /// 显示温度
        /// </summary>
        public enum ShowTemp : uint
        {
            不显示 = 0,
            显示 = 2,
            显示室内温度 = 1,
            显示室外温度 = 3
        }

        //除了29、31、34位为“1”外，均为“0”。其他位功能不详
        //在自动模式下只可以设置的项目有：风速1、2、3级、自动；上上下左右扫风；显示温度；灯光；睡眠定时（非睡眠）。其他项均不可以设置。此时温度不可设置，温度段的代码为：10011101。

        //public static byte[] Set(int temp = 26, Mode mode = Mode.自动, Power power = Power.开机, Speed speed = Speed.自动, Sweep sweep = Sweep.无扫风, Sleep sleep = Sleep.不睡眠, UltraStrong ultrastrong = UltraStrong.普通, Light light = Light.亮, Health health = Health.普通, Dry dry = Dry.普通, ShowTemp showtemp = ShowTemp.显示室内温度)
        public static byte[] Set(GeLi_ContronalParaMeter.ACPara para)
        {
            UInt64 cmd1 = 0;
            UInt64 cmd2 = 0;

            // *********CMD1***********
            // 1-3 模式
            cmd1 |= ((uint)para.Mode & 0x07) << 0;
            // 开关机
            cmd1 |= ((uint)para.Power & 0x01) << 3;
            // 风速
            cmd1 |= ((uint)para.Speed & 0x03) << 4;
            // 是否扫风
            cmd1 |= ((uint)para.Sweep >> 2 & 0x01) << 6;
            // 是否睡眠
            cmd1 |= ((uint)para.Sleep & 0x01) << 7;
            // 温度
            uint ptemp = Temperature.GetPreCode(para.Mode, para.temp);
            cmd1 |= ((uint)ptemp & 0x0f) << 8;
            // 睡眠，暂时设定为0
            cmd1 |= ((uint)0 & 0xff) << 12;
            // 超强
            cmd1 |= ((uint)para.UltraStrong & 0x01) << 20;
            // 灯光
            cmd1 |= ((uint)para.Light & 0x01) << 21;
            // 健康
            cmd1 |= ((uint)para.Health & 0x01) << 22;
            // 干燥，辅热
            cmd1 |= ((uint)para.Dry & 0x01) << 23;
            // 换气
            cmd1 |= ((uint)para.Health & 0x02) << 24;
            // 25位以后的部分，目前写死
            cmd1 |= (UInt64)0x128 << 25;

            // *********CMD2***********
            // 上下扫风
            cmd2 |= ((uint)para.Sweep >> 1 & 0x01) << 0;
            // 左右扫风
            cmd2 |= ((uint)para.Sweep & 0x01) << 4;
            // 显示温度
            cmd2 |= ((uint)para.ShowTemp & 0x03) << 8;
            // 命令2温度
            uint temp2 = Temperature.GetSufCode(para.Mode, para.temp);
            cmd2 |= temp2 << 28;

            byte[] commands = new byte[10];
            for (int i = 0; i < 5; i++)
                commands[i] = (byte)((cmd1 >> (i * 8)) & 0xff);

            for (int i = 0; i < 5; i++)
                commands[i + 5] = (byte)((cmd2 >> (i * 8)) & 0xff);

            return commands;
        }
    }
}
